///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file mpi_interface.cpp
///
/// \brief   definitions of mpi interface functions
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <src/pcps.h>
#include <src/mpi_interface.h>

#include <unistd.h> // needed for gethostname

#ifndef HAVE_MPI
MPI::Comm MPI::COMM_WORLD;
MPI::Op MPI::SUM;
MPI::Datatype MPI::CHAR;
MPI::Datatype MPI::SHORT;
MPI::Datatype MPI::INT;
MPI::Datatype MPI::LONG;
MPI::Datatype MPI::SIGNED_CHAR;
MPI::Datatype MPI::UNSIGNED_CHAR;
MPI::Datatype MPI::UNSIGNED_SHORT;
MPI::Datatype MPI::UNSIGNED;
MPI::Datatype MPI::UNSIGNED_LONG;
MPI::Datatype MPI::FLOAT;
MPI::Datatype MPI::DOUBLE;
MPI::Datatype MPI::LONG_DOUBLE;
MPI::Datatype MPI::BOOL;
MPI::Datatype MPI::COMPLEX;
MPI::Datatype MPI::DOUBLE_COMPLEX;
MPI::Datatype MPI::LONG_DOUBLE_COMPLEX;
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   initializes MPI
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::mpi::init(int argc, char **argv) {
  #ifdef HAVE_MPI
  MPI::Init(argc, argv);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   finalizes MPI
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::mpi::finalize() {
  #ifdef HAVE_MPI
  MPI::Finalize();
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   function to return the hostname for this process
///
/// \return the hostname as given by gethostname
///
///////////////////////////////////////////////////////////////////////////////////////////////////
std::string pcps::mpi::get_hostname() {

  // allocate vector to hold the character string
  std::vector<char> hostname_vec(1024);

  // load the hostname into the character string
  const int rc = gethostname(&hostname_vec.at(0), 1000);
  if (rc != 0)
    throw pcps::Exception("gethostname failed in pcps::mpi::get_hostname with error code %i") % rc;

  // return the hostname
  return std::string(&hostname_vec.at(0));

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   broadcasts data from one process to others
///
/// \param[in,out] data     a pointer to the data to be broadcast
/// \param[in]     n        the number of data elements
/// \param[in]     root     which processor to broadcast from
/// \param[in]     comm     the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::bcast(T * data, size_t n, int root, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 100000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Bcast((void *)data, block_size, pcps::mpi::datatype<T>(), root);
    data += block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Bcast((void *)data, int(n), pcps::mpi::datatype<T>(), root);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   reduces data on to one process
///
/// \param[in]     send_buf   a pointer to the data to be reduced
/// \param[out]    recv_buf   a pointer to where the reduced result will be stored
/// \param[in]     n          the number of data elements
/// \param[in]     op         the reduce operation to use
/// \param[in]     root       which processor to reduce to
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::reduce(const T * send_buf, T * recv_buf, size_t n,
                                         const MPI::Op & op, int root, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 100000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Reduce(send_buf, recv_buf, block_size, pcps::mpi::datatype<T>(), op, root);
    send_buf += block_size;
    recv_buf += block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Reduce(send_buf, recv_buf, int(n), pcps::mpi::datatype<T>(), op, root);
  #endif
  #ifndef HAVE_MPI
  std::copy(send_buf, send_buf+n, recv_buf);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   reduces data on to all processes
///
/// \param[in]     send_buf   a pointer to the data to be reduced
/// \param[out]    recv_buf   a pointer to where the reduced result will be stored
/// \param[in]     n          the number of data elements
/// \param[in]     op         the reduce operation to use
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::allreduce(const T * send_buf, T * recv_buf, size_t n,
                                            const MPI::Op & op, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 100000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Allreduce(send_buf, recv_buf, block_size, pcps::mpi::datatype<T>(), op);
    send_buf += block_size;
    recv_buf += block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Allreduce(send_buf, recv_buf, int(n), pcps::mpi::datatype<T>(), op);
  #endif
  #ifndef HAVE_MPI
  std::copy(send_buf, send_buf+n, recv_buf);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   sends data to another process
///
/// \param[in]     buf        a pointer to the data to be sent
/// \param[in]     n          the number of data elements
/// \param[in]     dest       rank of the process to send to
/// \param[in]     tag        a tag to label the transmission
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::send(const T * buf, size_t n, const int dest, const int tag, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 100000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Send(buf, block_size, pcps::mpi::datatype<T>(), dest, tag);
    buf += block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Send(buf, int(n), pcps::mpi::datatype<T>(), dest, tag);
  #endif
  #ifndef HAVE_MPI
  throw pcps::Exception("cannot use pcps::mpi::send without MPI");
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   receive data from another process
///
/// \param[out]    buf        a pointer to where the received data will be placed
/// \param[in]     n          the number of data elements
/// \param[in]     source     rank of the sending process
/// \param[in]     tag        a tag to label the transmission
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::recv(T * buf, size_t n, const int source, const int tag, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 100000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Recv(buf, block_size, pcps::mpi::datatype<T>(), source, tag);
    buf += block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Recv(buf, int(n), pcps::mpi::datatype<T>(), source, tag);
  #endif
  #ifndef HAVE_MPI
  throw pcps::Exception("cannot use pcps::mpi::recv without MPI");
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   scatter data from one process to others
///
/// \param[in]     send_buf   a pointer to the data to be scattered
/// \param[out]    recv_buf   a pointer to where the scattered data will be placed
/// \param[in]     n          the number of data elements each process should receive
/// \param[in]     root       which processor to scatter from
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::scatter(const T * send_buf, T * recv_buf, size_t n, int root, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 1000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Scatter(send_buf, block_size, pcps::mpi::datatype<T>(), recv_buf, block_size, pcps::mpi::datatype<T>(), root);
    send_buf += size_t(comm.Get_size()) * block_size;
    recv_buf += block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Scatter(send_buf, int(n), pcps::mpi::datatype<T>(), recv_buf, int(n), pcps::mpi::datatype<T>(), root);
  #endif
  #ifndef HAVE_MPI
  std::copy(send_buf, send_buf+n, recv_buf);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   gather data from all processes on to one
///
/// \param[in]     send_buf   a pointer to the data to be gathered
/// \param[out]    recv_buf   a pointer to where the gathered data will be placed
/// \param[in]     n          the number of data elements each process will send
/// \param[in]     root       which processor to gather to
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::gather(const T * send_buf, T * recv_buf, size_t n, int root, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 1000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Gather(send_buf, block_size, pcps::mpi::datatype<T>(), recv_buf, block_size, pcps::mpi::datatype<T>(), root);
    send_buf += block_size;
    recv_buf += size_t(comm.Get_size()) * block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Gather(send_buf, int(n), pcps::mpi::datatype<T>(), recv_buf, int(n), pcps::mpi::datatype<T>(), root);
  #endif
  #ifndef HAVE_MPI
  std::copy(send_buf, send_buf+n, recv_buf);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   gather data from all processes and place the result on all processes
///
/// \param[in]     send_buf   a pointer to the data to be gathered
/// \param[out]    recv_buf   a pointer to where the gathered data will be placed
/// \param[in]     n          the number of data elements each process will send
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::allgather(const T * send_buf, T * recv_buf, size_t n, const MPI::Comm & comm) {
  #ifdef HAVE_MPI
  const int block_size = 1000000/sizeof(T);
  while ( n > size_t(block_size) ) {
    comm.Allgather(send_buf, block_size, pcps::mpi::datatype<T>(), recv_buf, block_size, pcps::mpi::datatype<T>());
    send_buf += block_size;
    recv_buf += size_t(comm.Get_size()) * block_size;
    n -= size_t(block_size);
  }
  if (n > 0)
    comm.Allgather(send_buf, int(n), pcps::mpi::datatype<T>(), recv_buf, int(n), pcps::mpi::datatype<T>());
  #endif
  #ifndef HAVE_MPI
  std::copy(send_buf, send_buf+n, recv_buf);
  #endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   broadcasts a vector from one process to others
///
/// \param[in,out] v        the vector to be broadcast
/// \param[in]     root     which processor to broadcast from
/// \param[in]     comm     the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::bcast(std::vector<T> & v, int root, const MPI::Comm & comm) {
  size_t n = v.size();
  pcps::mpi::bcast(&n, 1, root, comm);
  v.resize(n);
  if (n > 0)
    pcps::mpi::bcast(&v.at(0), n, root, comm);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   reduces a vector on to one process
///
/// \param[in]     send_v     the vector to reduce
/// \param[out]    recv_v     the vector where the result of the reduction will be stored
/// \param[in]     op         the reduce operation to use
/// \param[in]     root       which processor to reduce to
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::reduce(const std::vector<T> & send_v, std::vector<T> & recv_v,
                                         const MPI::Op & op, int root, const MPI::Comm & comm) {
  const size_t n = send_v.size();
  if (pcps::mpi::rank() == root)
    recv_v.resize(n);
  if (n > 0)
    pcps::mpi::reduce(&send_v.at(0), &recv_v.at(0), n, op, root, comm);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   reduces a vector on to all processes
///
/// \param[in]     send_v     the vector to reduce
/// \param[out]    recv_v     the vector where the result of the reduction will be stored
/// \param[in]     op         the reduce operation to use
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::allreduce(const std::vector<T> & send_v, std::vector<T> & recv_v,
                                            const MPI::Op & op, const MPI::Comm & comm) {
  const size_t n = send_v.size();
  recv_v.resize(n);
  if (n > 0)
    pcps::mpi::allreduce(&send_v.at(0), &recv_v.at(0), n, op, comm);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   sends a vector to another process
///
/// \param[in]     v          the vector to send
/// \param[in]     dest       rank of the process to send to
/// \param[in]     tag        a tag to label the transmission
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::send(const std::vector<T> & v, const int dest, const int tag,
                                       const MPI::Comm & comm) {
  const size_t n = v.size();
  pcps::mpi::send(&n, 1, dest, tag, comm);
  if (n > 0)
    pcps::mpi::send(&v.at(0), n, dest, tag, comm);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   receive a vector from another process
///
/// \param[out]    v          the vector that the received vector will overwrite
/// \param[in]     source     rank of the sending process
/// \param[in]     tag        a tag to label the transmission
/// \param[in]     comm       the communicator to use (defaults to MPI::COMM_WORLD)
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::mpi::recv(std::vector<T> & v, const int source, const int tag,
                                       const MPI::Comm & comm) {
  size_t n;
  pcps::mpi::recv(&n, 1, source, tag, comm);
  v.resize(n);
  if (n > 0)
    pcps::mpi::recv(&v.at(0), n, source, tag, comm);
}

template void pcps::mpi::bcast(bool                  *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(char                  *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(signed short          *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(signed int            *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(signed long           *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(signed char           *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(unsigned char         *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(unsigned short        *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(unsigned int          *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(unsigned long int     *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(float                 *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(double                *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(long double           *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::complex<float>   *, size_t, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::complex<double>  *, size_t, int, const MPI::Comm &);

template void pcps::mpi::reduce(const char                *, char                *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const signed short        *, signed short        *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const signed int          *, signed int          *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const signed long         *, signed long         *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const signed char         *, signed char         *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const unsigned char       *, unsigned char       *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const unsigned short      *, unsigned short      *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const unsigned int        *, unsigned int        *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const unsigned long int   *, unsigned long int   *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const float               *, float               *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const double              *, double              *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const long double         *, long double         *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::complex<float> *, std::complex<float> *, size_t, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::complex<double>*, std::complex<double>*, size_t, const MPI::Op&, int, const MPI::Comm&);

template void pcps::mpi::allreduce(const char                *, char                *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const signed short        *, signed short        *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const signed int          *, signed int          *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const signed long         *, signed long         *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const signed char         *, signed char         *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const unsigned char       *, unsigned char       *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const unsigned short      *, unsigned short      *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const unsigned int        *, unsigned int        *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const unsigned long int   *, unsigned long int   *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const float               *, float               *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const double              *, double              *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const long double         *, long double         *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::complex<float> *, std::complex<float> *, size_t, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::complex<double>*, std::complex<double>*, size_t, const MPI::Op&, const MPI::Comm&);

template void pcps::mpi::send(const bool                *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const char                *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const signed short        *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const signed int          *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const signed long         *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const signed char         *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const unsigned char       *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const unsigned short      *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const unsigned int        *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const unsigned long int   *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const float               *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const double              *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const long double         *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const std::complex<float> *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::send(const std::complex<double>*, size_t, const int, const int, const MPI::Comm&);

template void pcps::mpi::recv(bool                *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(char                *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(signed short        *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(signed int          *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(signed long         *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(signed char         *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(unsigned char       *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(unsigned short      *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(unsigned int        *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(unsigned long int   *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(float               *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(double              *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(long double         *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(std::complex<float> *, size_t, const int, const int, const MPI::Comm&);
template void pcps::mpi::recv(std::complex<double>*, size_t, const int, const int, const MPI::Comm&);

template void pcps::mpi::scatter(const bool                *, bool                *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const char                *, char                *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const signed short        *, signed short        *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const signed int          *, signed int          *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const signed long         *, signed long         *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const signed char         *, signed char         *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const unsigned char       *, unsigned char       *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const unsigned short      *, unsigned short      *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const unsigned int        *, unsigned int        *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const unsigned long int   *, unsigned long int   *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const float               *, float               *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const double              *, double              *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const long double         *, long double         *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const std::complex<float> *, std::complex<float> *, size_t, int, const MPI::Comm&);
template void pcps::mpi::scatter(const std::complex<double>*, std::complex<double>*, size_t, int, const MPI::Comm&);

template void pcps::mpi::gather(const bool                *, bool                *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const char                *, char                *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const signed short        *, signed short        *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const signed int          *, signed int          *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const signed long         *, signed long         *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const signed char         *, signed char         *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const unsigned char       *, unsigned char       *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const unsigned short      *, unsigned short      *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const unsigned int        *, unsigned int        *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const unsigned long int   *, unsigned long int   *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const float               *, float               *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const double              *, double              *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const long double         *, long double         *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const std::complex<float> *, std::complex<float> *, size_t, int, const MPI::Comm&);
template void pcps::mpi::gather(const std::complex<double>*, std::complex<double>*, size_t, int, const MPI::Comm&);

template void pcps::mpi::allgather(const bool                *, bool                *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const char                *, char                *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const signed short        *, signed short        *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const signed int          *, signed int          *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const signed long         *, signed long         *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const signed char         *, signed char         *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const unsigned char       *, unsigned char       *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const unsigned short      *, unsigned short      *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const unsigned int        *, unsigned int        *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const unsigned long int   *, unsigned long int   *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const float               *, float               *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const double              *, double              *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const long double         *, long double         *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const std::complex<float> *, std::complex<float> *, size_t, const MPI::Comm&);
template void pcps::mpi::allgather(const std::complex<double>*, std::complex<double>*, size_t, const MPI::Comm&);

template void pcps::mpi::bcast(std::vector<char                  > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<signed short          > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<signed int            > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<signed long           > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<signed char           > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<unsigned char         > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<unsigned short        > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<unsigned int          > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<unsigned long int     > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<float                 > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<double                > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<long double           > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<std::complex<float>   > &, int, const MPI::Comm &);
template void pcps::mpi::bcast(std::vector<std::complex<double>  > &, int, const MPI::Comm &);

template void pcps::mpi::reduce(const std::vector<char                 >&, std::vector<char                 >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<signed short         >&, std::vector<signed short         >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<signed int           >&, std::vector<signed int           >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<signed long          >&, std::vector<signed long          >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<signed char          >&, std::vector<signed char          >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<unsigned char        >&, std::vector<unsigned char        >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<unsigned short       >&, std::vector<unsigned short       >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<unsigned int         >&, std::vector<unsigned int         >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<unsigned long int    >&, std::vector<unsigned long int    >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<float                >&, std::vector<float                >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<double               >&, std::vector<double               >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<long double          >&, std::vector<long double          >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<std::complex<float>  >&, std::vector<std::complex<float>  >&, const MPI::Op&, int, const MPI::Comm&);
template void pcps::mpi::reduce(const std::vector<std::complex<double> >&, std::vector<std::complex<double> >&, const MPI::Op&, int, const MPI::Comm&);

template void pcps::mpi::allreduce(const std::vector<char                 >&, std::vector<char                 >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<signed short         >&, std::vector<signed short         >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<signed int           >&, std::vector<signed int           >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<signed long          >&, std::vector<signed long          >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<signed char          >&, std::vector<signed char          >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<unsigned char        >&, std::vector<unsigned char        >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<unsigned short       >&, std::vector<unsigned short       >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<unsigned int         >&, std::vector<unsigned int         >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<unsigned long int    >&, std::vector<unsigned long int    >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<float                >&, std::vector<float                >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<double               >&, std::vector<double               >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<long double          >&, std::vector<long double          >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<std::complex<float>  >&, std::vector<std::complex<float>  >&, const MPI::Op&, const MPI::Comm&);
template void pcps::mpi::allreduce(const std::vector<std::complex<double> >&, std::vector<std::complex<double> >&, const MPI::Op&, const MPI::Comm&);

template void pcps::mpi::send(const std::vector<char                  > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<signed short          > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<signed int            > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<signed long           > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<signed char           > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<unsigned char         > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<unsigned short        > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<unsigned int          > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<unsigned long int     > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<float                 > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<double                > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<long double           > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<std::complex<float>   > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::send(const std::vector<std::complex<double>  > &, const int, const int, const MPI::Comm &);

template void pcps::mpi::recv(std::vector<char                  > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<signed short          > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<signed int            > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<signed long           > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<signed char           > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<unsigned char         > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<unsigned short        > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<unsigned int          > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<unsigned long int     > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<float                 > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<double                > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<long double           > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<std::complex<float>   > &, const int, const int, const MPI::Comm &);
template void pcps::mpi::recv(std::vector<std::complex<double>  > &, const int, const int, const MPI::Comm &);

